package com.cjm.one.goods.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cjm.one.common.config.redission.DistributedLockTemplate;
import com.cjm.one.common.exception.GlobalException;
import com.cjm.one.common.result.Result;
import com.cjm.one.common.result.ResultCodeEnum;
import com.cjm.one.goods.mapper.CouponInfoMapper;
import com.cjm.one.goods.mapper.CustomerCouponMapper;
import com.cjm.one.goods.service.CustomerCouponService;
import com.cjm.one.model.coupon.dto.CouponConfirmDto;
import com.cjm.one.model.coupon.dto.CouponReserveDto;
import com.cjm.one.model.coupon.entity.CouponInfo;
import com.cjm.one.model.coupon.entity.CustomerCoupon;
import com.cjm.one.model.coupon.vo.CouponInfoVo;
import com.cjm.one.model.coupon.vo.CouponReserveVo;
import jakarta.annotation.Resource;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
* @author 29289
* @description 针对表【customer_coupon(乘客优惠券关联表)】的数据库操作Service实现
* @createDate 2024-09-05 15:59:26
*/
@Service
public class CustomerCouponServiceImpl extends ServiceImpl<CustomerCouponMapper, CustomerCoupon>
    implements CustomerCouponService {
    @Resource
    private CustomerCouponMapper customerCouponMapper;
    @Resource
    private  CouponInfoMapper couponInfoMapper;
    @Resource
    private RedissonClient redissonClient;

    @Resource
    private DistributedLockTemplate lockTemplate;

    @Override
    public Page<CustomerCoupon> listPage(Integer current, Integer size, String keyword, Integer status,Long userId) {
        return customerCouponMapper.listPage(new Page<CustomerCoupon>(current,size),keyword,status,userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean receiveCoupon(CustomerCoupon customerCoupon) {
        Long couponId = customerCoupon.getCouponId();
        Long customerId = customerCoupon.getCustomerId();

        // 第一步：校验领取资格
        CouponInfo coupon = couponInfoMapper.selectById(couponId);
        checkCouponValid(coupon, customerId); // 封装校验方法

        // 第二步：乐观锁更新库存
        int updateRows = couponInfoMapper.updateCouponStock(
                couponId,
                coupon.getVersion(),
                LocalDateTime.now()
        );

        if (updateRows == 0) {
            throw new GlobalException(ResultCodeEnum.COUPON_STOCK_OUT);
        }

        // 第三步：写入领取记录
        return customerCouponMapper.insert(customerCoupon) > 0;
    }
    private void checkCouponValid(CouponInfo coupon, Long customerId) {
        // 基础状态校验
        if (coupon.getStatus() != 1) {
            throw new GlobalException( ResultCodeEnum.COUPON_NO_RECEIVE );
        }

        // 库存校验
        if (coupon.getPublishCount() > 0
                && coupon.getReceiveCount() >= coupon.getPublishCount()) {
            throw new GlobalException(ResultCodeEnum.COUPON_LESS);
        }

        // 限领校验（提前快速失败）
        if (coupon.getPerLimit() > 0
                && customerCouponMapper.selectReceivedCount(coupon.getId(), customerId) >= coupon.getPerLimit()) {
            throw new GlobalException(ResultCodeEnum.COUPON_USER_LIMIT);
        }
    }

    @Override
    public Page<CouponInfoVo> getCouponInfoAppReceivedPage(Long current, Long size,String keyWord, Integer rangeType, Long customerId) {
        Page<CouponInfoVo> page = new Page<>(current, size);
        Page<CouponInfoVo> Vopage = customerCouponMapper.selectCouponInfoAppReceivedPage(page,keyWord, rangeType, customerId);

        return Vopage;
    }

    @Override
    public CouponReserveVo processReserve(CouponReserveDto couponReserveDto) {
        List<Long> successIds = new ArrayList<>();
        Map<Long, String> failReasons = new ConcurrentHashMap<>();

        // 批量查询优惠券信息
        List<CouponInfo> couponInfos = couponInfoMapper.selectBatchIds(couponReserveDto.getCouponIds());
        Map<Long, CouponInfo> couponInfoMap = couponInfos.stream()
                .collect(Collectors.toMap(CouponInfo::getId, Function.identity()));

        // 并行处理每个优惠券
        couponReserveDto.getCouponIds().parallelStream().forEach(couponId -> {
            RLock lock = redissonClient.getLock("coupon:lock:" + couponReserveDto.getUserId() + ":" + couponId);
            try {
                if (lock.tryLock(2, 3, TimeUnit.SECONDS)) {
                    processSingleCoupon(couponReserveDto, couponInfoMap.get(couponId), successIds, failReasons);
                }
            } catch (InterruptedException e) {
                failReasons.put(couponId, "系统繁忙，请重试");
            } finally {
                lock.unlock();
            }
        });

        return new CouponReserveVo(successIds, failReasons);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result<Boolean> confirmUsage(CouponConfirmDto dto) {
        // 批量更新用户券状态
        int updateCount = customerCouponMapper.update(new LambdaUpdateWrapper<CustomerCoupon>()
                .in(CustomerCoupon::getCouponId, dto.getCouponIds())
                .eq(CustomerCoupon::getCustomerId, dto.getUserId())
                .eq(CustomerCoupon::getStatus, 1) // TODO：当前先设置为1 ,后续添加优惠卷预占是需要修改为0 仅处理状态为1的优惠券
                .set(CustomerCoupon::getStatus, 2)
                .set(CustomerCoupon::getUsedTime, LocalDateTime.now())
                .set(CustomerCoupon::getOrderId, dto.getOrderId()));

        if (updateCount != dto.getCouponIds().size()) {
            throw new GlobalException(ResultCodeEnum.COUPON_EXPESION);
        }

        // 批量更新优惠券使用计数
        couponInfoMapper.batchIncrUseCount(dto.getCouponIds());
        return Result.ok(true);
    }

    @Override
    public Boolean autoReceiveCoupon(CouponReserveDto couponReserveDto) {
        List<Long> couponIds = couponReserveDto.getCouponIds();
        Long customerId = couponReserveDto.getUserId();

        // 1. 使用 Redisson 分布式锁（按用户ID加锁）
        String lockKey = "coupon:batch:lock:" + customerId;
        return lockTemplate.execute(lockKey, 5, TimeUnit.SECONDS, () -> {
            // 2. 查询优惠券信息（带校验）
            List<CouponInfo> coupons = couponInfoMapper.selectBatchIds(couponIds);
            if (coupons.size() != couponIds.size()) {
                throw new GlobalException(ResultCodeEnum.COUPON_NOT_EXIST);
            }

            // 3. 预校验库存和限领规则
            coupons.forEach(coupon -> checkCouponValid(coupon, customerId));

            // 4. 批量扣减库存由分布式锁保证原子性）
            couponInfoMapper.incrementBatchReceiveCount(couponIds);

            // 5. 生成领取记录
            List<CustomerCoupon> records = coupons.stream()
                    .map(coupon -> {
                        CustomerCoupon cc = new CustomerCoupon();
                        cc.setCustomerId(customerId);
                        cc.setCouponId(coupon.getId());
                        cc.setReceiveTime(LocalDateTime.now());
                        cc.setStatus(1);
                        return cc;
                    })
                    .collect(Collectors.toList());

            boolean success = customerCouponMapper.insertBatchSomeColumn(records) == couponIds.size();
            return Result.ok(success);
        }).getData(); // 从Result中提取布尔值
    }

    private void processSingleCoupon(CouponReserveDto dto, CouponInfo info,
                                     List<Long> successIds, Map<Long, String> failReasons) {
        try {
            // 1. 检查用户是否已领取
            CustomerCoupon userCoupon = customerCouponMapper.selectOne(new LambdaQueryWrapper<CustomerCoupon>()
                    .eq(CustomerCoupon::getCustomerId, dto.getUserId())
                    .eq(CustomerCoupon::getCouponId, info.getId())
                    .last("LIMIT 1"));

            // 2. 自动领取逻辑
            if (userCoupon == null && dto.getAutoReceive()) {
                if (!tryAutoReceive(dto.getUserId(), info)) {
                    failReasons.put(info.getId(), "自动领取失败");
                    return;
                }
                userCoupon = customerCouponMapper.selectById(info.getId());
            }

            // 3. 有效性校验
            if (!validateCoupon(info, userCoupon)) {
                failReasons.put(info.getId(), "优惠券不可用");
                return;
            }

            // 4. 执行预占
            int updateCount = customerCouponMapper.update(null,
                    new LambdaUpdateWrapper<CustomerCoupon>()
                            .eq(CustomerCoupon::getId, userCoupon.getId())
                            .eq(CustomerCoupon::getStatus, 1)
                            .set(CustomerCoupon::getStatus, 0)
                            .set(CustomerCoupon::getOrderId, dto.getOrderId()));

            if (updateCount > 0) {
                successIds.add(info.getId());
                couponInfoMapper.incrUseCount(info.getId());
            }
        } catch (Exception e) {
            failReasons.put(info.getId(), "系统异常");
        }
    }
    private boolean tryAutoReceive(Long userId, CouponInfo info) {
        // 领取数量校验
        Long receivedCount = customerCouponMapper.selectCount(new LambdaQueryWrapper<CustomerCoupon>()
                .eq(CustomerCoupon::getCustomerId, userId)
                .eq(CustomerCoupon::getCouponId, info.getId()));

        if (info.getPerLimit() > 0 && receivedCount >= info.getPerLimit()) {
            return false;
        }

        // 插入用户券记录
        CustomerCoupon newCoupon = new CustomerCoupon();
        newCoupon.setCustomerId(userId);
        newCoupon.setCouponId(info.getId());
        newCoupon.setStatus(1);
        newCoupon.setReceiveTime(LocalDateTime.now());
        newCoupon.setExpireTime(info.getExpireTime());
        customerCouponMapper.insert(newCoupon);

        // 更新优惠券领取计数
        couponInfoMapper.incrReceiveCount(info.getId());
        return true;
    }

    private boolean validateCoupon(CouponInfo info, CustomerCoupon userCoupon) {
        return userCoupon != null
                && userCoupon.getStatus() == 1
                && LocalDateTime.now().isBefore(userCoupon.getExpireTime())
                && info.getStatus() == 1
                && (info.getPublishCount() == 0 || info.getUseCount() < info.getPublishCount());
    }
}




